package com.tally.business.controller;

import com.tally.business.mq.MqProducer;
import com.tally.server.config.ConstKey;
import com.tally.server.dto.CostBatchDTO;
import com.tally.server.dto.CostDTO;
import com.tally.server.dto.EverydayCostDTO;
import com.tally.server.dto.ResponseDTO;
import com.tally.server.enums.ExpTypeEnum;
import com.tally.server.exception.ValidatorException;
import com.tally.server.service.CostService;
import com.tally.server.utils.DateUtil;
import com.tally.server.utils.ValidatorUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: sy
 * @Date: 2020/11/13 20:13
 * @Description:
 */
@RestController
@RequestMapping("/cost")
public class CostController extends BusinessController {

    private static final Logger logger = LoggerFactory.getLogger(CostController.class);
    public static final String BUSINESS_NAME = "支出";

    @Autowired
    private CostService costService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    @Value("${key.expire-time.graph}")
    private Long graphExpireTime;

    /**
     * 保存，id有值时更新，无值时新增
     * @param costDTO
     * @return
     */
    @PostMapping("/save")
    public ResponseDTO save(@RequestBody CostDTO costDTO){
        Integer userId;
        Date recordDate;
        // 保存校验
        ValidatorUtil.require(userId = costDTO.getUserId(),"用户id");
        ValidatorUtil.require(recordDate = costDTO.getRecordDate(),"日期");
        ValidatorUtil.require(costDTO.getCostType(),"支出类型");
        ValidatorUtil.require(costDTO.getMoney(), "金额");

        // ---异步
        delKeys(userId, recordDate);
        // ---

        int type = costService.save(costDTO);
        if(type == 1){
            mqProducer.addExp(userId, ExpTypeEnum.EXP_BUSINESS.getCode());
        }

        return ResponseDTO.success(costDTO);
    }

    /**
     * 批量保存
     * @param costBatchDTO
     * @return
     */
    @PostMapping("/save-batch")
    public ResponseDTO saveBatch(@RequestBody CostBatchDTO costBatchDTO) {
        Integer userId;
        Date recordStartDate, recordEndDate;
        // 保存校验
        ValidatorUtil.require(userId = costBatchDTO.getUserId(),"用户id");
        ValidatorUtil.require(recordStartDate = costBatchDTO.getRecordStartDate(),"开始日期");
        ValidatorUtil.require(recordEndDate = costBatchDTO.getRecordEndDate(),"结束日期");
        ValidatorUtil.require(costBatchDTO.getCostType(),"支出类型");
        ValidatorUtil.require(costBatchDTO.getMoney(), "金额");

        costService.saveBatch(costBatchDTO);
        Set<Date> dateSet = DateUtil.dateMonthList(recordStartDate, recordEndDate);
        for (Date recordDate: dateSet) {
            delKeys(userId, recordDate);
        }
        return ResponseDTO.success(null);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{id}")
    public ResponseDTO delete(@PathVariable String id){
        CostDTO costDTO = costService.selectById(id);

        // --- 异步
        delKeys(costDTO.getUserId(),costDTO.getRecordDate());
        // ---

        costService.delete(id);
        return ResponseDTO.success(null);
    }

    /**
     * 得到某年某月的支出列表
     * @param year
     * @param month
     * @param userId
     * @return
     */
    @GetMapping("/get-month-cost/{year}/{month}/{userId}")
    public ResponseDTO getMonthCost(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            return ResponseDTO.success(null);
        }
        int yearI = parseInt(year);
        int monthI = parseInt(month);
        List<CostDTO> costDTOList = costService.getMonthCost(yearI,monthI,userId,true);
        return ResponseDTO.success(costDTOList);
    }

    /**
     * 得到某年的总支出金额
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/get-cost-sum/{year}/{userId}")
    public ResponseDTO getCostSum(@PathVariable String year, @PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            return ResponseDTO.success(BigDecimal.ZERO);
        }
        int yearI = parseInt(year);
        BigDecimal sum = costService.getYearSum(yearI, userId);
        return ResponseDTO.success(sum);
    }

    /**
     * 得到某年某月的总支出金额
     * @param year
     * @param month
     * @param userId
     * @return
     */
    @GetMapping("/get-cost-sum/{year}/{month}/{userId}")
    public ResponseDTO getCostSum(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        int yearI = parseInt(year);
        int monthI = parseInt(month);
        BigDecimal sum = costService.getMonthSum(yearI, monthI, userId);
        return ResponseDTO.success(sum);
    }

    /**
     * 某月每天的支出总和
     * @param year
     * @param month
     * @param userId
     * @return [0,100,23.3,0,45,0...]
     */
    @GetMapping("/everyday-cost-sum/{year}/{month}/{userId}")
    public ResponseDTO everydayCostSum(@PathVariable String year, @PathVariable String month, @PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            throw new ValidatorException("参数出现空值");
        }
        // 先查redis，若不存在key则查询数据库
        ListOperations listOperations = redisTemplate.opsForList();
        String listKey = generateKey("s s s s",ConstKey.GRAPH_COST_LINE_PREFIX,userId,year,month);
        if(redisTemplate.hasKey(listKey)){
            List<Integer> listRedis = listOperations.range(listKey, 0, listOperations.size(listKey));
            return ResponseDTO.success(listRedis);
        }

        int yearI = parseInt(year);
        int monthI = parseInt(month);
        List<BigDecimal> list = costService.everydayCostSum(yearI,monthI,userId);
        // 查完数据库后将数据写到redis，关联用户id，并设置过期时间，如：graph-cost-line-{userId}-{year}-{month}, 5min
        listOperations.rightPushAll(listKey,list);
        redisTemplate.expire(listKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(list);
    }

    /**
     * 得到某年某月的各项支出总和
     * @param year
     * @param month
     * @param userId
     * @return map
     */
    @GetMapping("/each-cost-sum/{year}/{month}/{userId}")
    public ResponseDTO eachCostSum(@PathVariable String year,@PathVariable String month,@PathVariable String userId){
        if(StringUtils.isEmpty(year) || StringUtils.isEmpty(month)){
            throw new ValidatorException("参数出现空值");
        }
        // 查询redis
        HashOperations hashOperations = redisTemplate.opsForHash();
        String hashKey = generateKey("s s s s",ConstKey.GRAPH_COST_PIE_PREFIX,userId,year,month);
        if(redisTemplate.hasKey(hashKey)){
            Map<String,BigDecimal> mapRedis = hashOperations.entries(hashKey);
            return ResponseDTO.success(mapRedis);
        }

        int yearI = parseInt(year);
        int monthI = parseInt(month);
        Map<String,BigDecimal> map = costService.eachCostSum(yearI,monthI,userId);
        // 写到redis
        hashOperations.putAll(hashKey,map);
        redisTemplate.expire(hashKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(map);
    }

    /**
     * 得到某年各项支出总和
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/each-cost-sum/{year}/{userId}")
    public ResponseDTO eachCostSum(@PathVariable String year,@PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            throw new ValidatorException("参数出现空值");
        }
        // 查询redis
        String hashKey = generateKey("s s s",ConstKey.GRAPH_COST_PIE_PREFIX,userId,year);
        HashOperations hashOperations = redisTemplate.opsForHash();
        if(redisTemplate.hasKey(hashKey)){
            Map<String,BigDecimal> mapRedis = hashOperations.entries(hashKey);
            return ResponseDTO.success(mapRedis);
        }
        int yearI = parseInt(year);
        Map<String,BigDecimal> map = costService.eachCostSum(yearI,userId);
        // 写到redis
        hashOperations.putAll(hashKey,map);
        redisTemplate.expire(hashKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(map);
    }

    /**
     * 得到某年每月支出总和
     * @param year
     * @param userId
     * @return
     */
    @GetMapping("/month-cost-sum/{year}/{userId}")
    public ResponseDTO monthCostSum(@PathVariable String year,@PathVariable String userId){
        if(StringUtils.isEmpty(year)){
            throw new ValidatorException("参数出现空值");
        }
        // 先查redis，若不存在key则查询数据库
        ListOperations listOperations = redisTemplate.opsForList();
        String listKey = generateKey("s s s",ConstKey.GRAPH_COST_BAR_PREFIX,userId,year);
        if(redisTemplate.hasKey(listKey)){
            List<Integer> listRedis = listOperations.range(listKey, 0, listOperations.size(listKey));
            return ResponseDTO.success(listRedis);
        }
        int yearI = parseInt(year);
        List<BigDecimal> sumList = costService.monthCostSum(yearI,userId);
        // 写到redis
        listOperations.rightPushAll(listKey,sumList);
        redisTemplate.expire(listKey,graphExpireTime, TimeUnit.SECONDS);
        return ResponseDTO.success(sumList);
    }

    @GetMapping("/view")
    public ResponseDTO viewCost(@RequestParam("start") String start,
                                @RequestParam("end") String end) throws ParseException {
        Date startDate = DateUtils.parseDate(start, "yyyy-MM-dd");
        Date endDate = DateUtils.parseDate(end, "yyyy-MM-dd");
        Integer userId = getLoginUser().getInteger("id");
        List<EverydayCostDTO> everydayCostDTOList = costService.getEverydayList(startDate, endDate, userId);
        return ResponseDTO.success(everydayCostDTOList);
    }

    private void delKeys(Integer userId, Date recordDate) {
        int year = getYearAndMonth(recordDate)[0];
        int month = getYearAndMonth(recordDate)[1];
        String key1 = generateKey("s * s s s", ConstKey.GRAPH_COST_PREFIX,userId.toString(),String.valueOf(year),String.valueOf(month));
        String key2 = generateKey("s * s s",ConstKey.GRAPH_COST_PREFIX,userId.toString(),String.valueOf(year));
        String[] keys = new String[]{key1,key2};
        for(String key:keys){
            logger.info("key={}",key);
            Set keySet = redisTemplate.keys(key);
            redisTemplate.delete(keySet);
        }
    }
}
